"""
a gnugo interface.  scope:
 - use twisted
 - launch a gnugo process
 - talk to it with GTP
 - use a gametree object
 - pass gametree events to GTP/gnugo
 - convert GTP messages into gametree events

right now, when run, this file runs two gnugo instances against each other on a 13x13 board.
"""

"""
TODO:
- switch to real Game instance, and send real (gobject) signals to change things
- hook up to pygtk2 reactor <http://twistedmatrix.com/projects/core/documentation/howto/choosing-reactor.html>
  - then hook up glboard.py to gametree events!
- add a level= arg to GTP
- FUTURE: add undo support...
- FUTURE: loading existing games (load_sgf?)
"""

from twisted.internet import reactor, protocol

from board import BLACK, WHITE
from point import Point


# ----------------------------------------
# FUTURE: from game import Game; in the meantime, i can see what interface i want from it
class Game(object):
    """a dummy Game."""
    # FIXME: real Game() requires root-node -- is None ok?
    def __init__(self):
        self.passes = 0  # number of consecutive passes

    # poor man's event system:
    def i_moved(self, color, pt):  # pt = a Point, or None=pass
        if color == BLACK:
            self.white.opp_moved(pt)
        else:
            self.black.opp_moved(pt)

        # this will be in the game logic, later -- the rules (sub)class (because each ruleset could have its own way to determine when the game is over)
        if pt:
            self.passes = 0
        else:
            self.passes += 1

    def is_over(self):
        return self.passes >= 2
# ----------------------------------------


def color2str(c):  # better to make Color a class?
    """turn a color (BLACK or WHITE) into a (lower-case) string."""
    if c == BLACK:
        return "black"
    elif c == WHITE:
        return "white"
    else:
        raise ValueError("bad arg to color2str()")
def opposite(c):
    if c == BLACK:
        return WHITE
    elif c == WHITE:
        return BLACK
    else:
        raise ValueError("bad arg to opposite()")

class GoTextProtocol(protocol.ProcessProtocol):
    def __init__(self, game, color, boardsize, komi, handicap=None):  # handicap = list of Point
        self.game = game
        self.color = color
        self.boardsize = boardsize  # gnugo 3.6 requires this in the range 5..19
        self.komi = komi
        self.handicap = handicap or []

        # HOW TO SET THIS UP?
        if color == BLACK:
            self.game.black = self
        else:
            self.game.white = self

        self.buffer = ""

    def opp_moved(self, pt):
        # play it on my board
        if pt:
            self._say("play %s %s" % (color2str(opposite(self.color)), pt))
        else:
            self._say("play %s pass" % color2str(opposite(self.color)))

        # now it's my turn again
        self._genmove()

    def _say(self, message):
        # FUTURE: give it an id number?  ("123 genmove white" => "=123 D4")
        self.transport.write(message + "\n")

    def connectionMade(self):
        # set things up
        self._say("boardsize %d" % self.boardsize)
        self._say("komi %f" % self.komi)
        if self.handicap:
            self._say("set_free_handicap %s" % " ".join(self.handicap))

        # make the first move
        if (self.color == BLACK and self.handicap == []) or \
           (self.color == WHITE and self.handicap != []):
            self._genmove()

    def _genmove(self):
        self._say("genmove %s" % color2str(self.color))

    def outReceived(self, data):
        self.buffer += data

        while "\n" in self.buffer:
            next, self.buffer = self.buffer.split("\n", 1)
            self._process(next)

    def _process(self, data):
        if data == '':
            return  # skip empty lines
        if data[0] == '=':
            message = data[1:].strip()
            if message:
                print "%d: good, msg=%r" % (self.color, message)

                if message == 'PASS':
                    self.game.i_moved(self.color, None)
                else:
                    # ASSUME it's from a GENMOVE, then:
                    pt = Point(str=message)
                    self.game.i_moved(self.color, pt)
        elif data[0] == '?':
            print "bad!"
            print "data=%r" % data
        else:
            print "i have no idea what this means:"
            print "data=%r" % data

        if self.game.is_over():
            print "GAME OVER!"
            self._say("quit")  # ALSO, send to self.game so opponent quits, too?
            reactor.stop()

    def processEnded(self, status):
        print 'process ended'  # status is a ProcessDone instance


if __name__ == '__main__':
    game = Game()
    p1 = reactor.spawnProcess(GoTextProtocol(game, BLACK, 13, 5.5),
                              'gnugo', ['gnugo', '--mode=gtp'], env=None)
    p2 = reactor.spawnProcess(GoTextProtocol(game, WHITE, 13, 5.5),
                              'gnugo', ['gnugo', '--mode=gtp'], env=None)
    reactor.run()
